package roborally;

import java.util.HashSet;
import java.util.List;
import java.util.ArrayList;
import java.util.Random;

import be.kuleuven.cs.som.annotate.Basic;
import be.kuleuven.cs.som.annotate.Model;
import be.kuleuven.cs.som.annotate.Raw;
import roborally.Exceptions.IllegalDestinationException;


/**
 * A class of surpriseboxes, that extends the class carryable. Supriseboxes only have a weight.
 * 
 * @version  2.90
 * @author   Michiel Ruelens & Dieter Naten
 */
public class SurpriseBox extends Carryable {
	
	//Inherits 'boolean isBeingCarried'
	//Inherits 'long x'
	//Inherits 'long y'
	//Inherits 'Board board'
	//Inherits 'String type'

	/**
     * Initialize the new surprisebox with given weight.
     * 
     * @param  	weight
     * 		   	The weight of the new suprisebox
     * @effect 	| setPlaceableType(PlaceableType.SURPRISEBOX)
     */
	@Raw
	public SurpriseBox(int weight){
		super(weight); //Sets terminated status to false
		setPlaceableType(PlaceableType.SURPRISEBOX);
	}
	
	/**
	 * This method puts the current surprisebox on a board.
	 * 
	 * @param 	board
	 * 		  	The board on which the sutprisebox should be placed
	 * @param 	x
	 * 		  	The x-coordinate on which the sutprisebox should be placed
	 * @param 	y
	 * 		  	The y-coordinate on which the sutprisebox should be placed
	 * @effect | putPlaceable(board,x,y)
	 */
	public void putSurpriseBox(Board board, long x, long y) {
		putPlaceable(board,x,y);
	}

	/**
	 * This method is executed when a surprisebox is hit.
	 * 
	 * @effect 	All objects surrounding the surprisebox are hit. 
	 * 		 	The hitPlaceables list, contains all objects surrounding the current surpisebox
	 * 			|	for(Placeable p: hitPlaceables){
	 *			|		if(p==this){ p.terminate()	}
	 *			|		else	   { p.hit() 		}
	 */
	@Override
	public void hit(){
		Board board = this.getBoard();
		System.out.println(board);
		HashSet<Placeable> hitPlaceables = new HashSet<Placeable>();
		List<Coordinate> adjacentCoordinatesList = Coordinate.getAllAdjacentCoordinates(this.getCoordinate());
		for(Coordinate c: adjacentCoordinatesList){
			if(Coordinate.isValidAndOpenCoordinate(board, c.getX(), c.getY())){
				if(board.hasPlaceableAtPosition(c.getX(), c.getY())){
					hitPlaceables.addAll(board.getAllObjectsByPosition(c.getX(), c.getY()));
				}
			}
		}
		for(Placeable p: hitPlaceables){
			if(p==this){
				p.terminate();
			}
			else{
				p.hit();
			}
		}
	}
	
	/**
	 * Method executed when a robot uses a surprisebox.
	 * 
	 * @param   robot
	 * 			The robot that uses the current surprisebox
	 * @pre		| !this.isTerminated()
	 * @pre		| robot.getAllPossessions().contains(this)
	 * @effect  | int randomInt = random.nextInt(5);
	 * 			|	if(randomInt == 0) { robot.hit(); }
	 * 			|	if(randomInt == 1) { teleport(robot); }
	 * 			|	if(randomInt == 2) { kinderSurprise(PlaceableType.BATTERY, robot); }
	 * 			|	if(randomInt == 3) { kinderSurprise(PlaceableType.SURPRISEBOX, robot); }
	 * 			|	if(randomInt == 4) { kinderSurprise(PlaceableType.REPAIRKIT, robot); }
	 * @effect 	| robot.removeCarryableFromPossessions(this)
	 * @effect  | this.terminate()
	 */
	@Override
	public void useCarryable(Robot robot){
		assert(!this.isTerminated());
		assert(robot.getAllPossessions().contains(this));
		Random random = new Random();
		int randomInt = random.nextInt(5);
		if(randomInt == 0){
			robot.hit();
			System.out.println("Your suprisebox has exploded!");
		}
		if(randomInt == 1){
			teleport(robot);
			System.out.println("You've been teleported to a random new position!");
		}
		if(randomInt == 2){
			kinderSurprise(PlaceableType.BATTERY, robot);
			System.out.println("A new battery has been added to your possessions!");
		}
		if(randomInt ==3){
			kinderSurprise(PlaceableType.SURPRISEBOX, robot);
			System.out.println("A new suprisebox has been added to your possessions!");
		}
		if(randomInt == 4){
			kinderSurprise(PlaceableType.REPAIRKIT, robot);
			System.out.println("A new repairkit has been added to your possessions!");
		}
		robot.removeCarryableFromPossessions(this);
		this.terminate();
	}
	
	/**
	 * Method evoked when after opening a surprisebox, a new carryable should be added to the given robots possessions.
	 * 
	 * @param 	type
	 * 			| The type of carryable that should be added to the robots possessions.
	 * @param 	robot
	 * 			| The robot that should get a carryable of the given type.
	 * @effect 	| if(type == PlaceableType.BATTERY)
	 * 			| robot.addCarryableToPossessions(new Battery(5000, 1500))
	 * @effect 	| if(type == PlaceableType.REPAIRKIT)
	 * 			| robot.addCarryableToPossessions(new RepairKit(5000, 1500))
	 * @effect 	| if(type == PlaceableType.SURPRISEBOX)
	 * 			| robot.addCarryableToPossessions(new SurpriseBox(5000, 1500))
	 */
	@Model
	private void kinderSurprise(PlaceableType type, Robot robot) {
		if(type == PlaceableType.BATTERY){
			Battery battery = new Battery(5000, 1500);
			robot.addCarryableToPossessions(battery);
		}
		else if(type == PlaceableType.REPAIRKIT){
			RepairKit repairkit = new RepairKit(5000, 1500);
			robot.addCarryableToPossessions(repairkit);
		}
		else if(type == PlaceableType.SURPRISEBOX){
			SurpriseBox suprisebox = new SurpriseBox(1500);
			robot.addCarryableToPossessions(suprisebox);
		}
	}
	
	/**
	 * When a suprisebox is used, the robot that used it, might be teleported by this method.
	 * 
	 * @effect	robot.setCoordinate(random.nextInt((int)board.getMaxWidth()), random.nextInt((int)board.getMaxHeight()))
	 */
	public void teleport(Robot robot){
		Random random = new Random();
		Board robotBoard = robot.getBoard();
		long randomXInt = random.nextInt(100000);
		long randomYInt = random.nextInt(100000);
		robotBoard.removePlaceable(robot);
		robot.setCoordinate(randomXInt, randomYInt);
		robotBoard.addPlaceable(robot);
	}
}